import BenefitAPI from '@/api/Benefit';
import {
  Box,
  DragAndDropProvider,
  DraggableItem,
  DragHandle,
  DroppableContainer,
  Icon,
  useCall,
  useReq,
  useSet,
  useSimpleMemo,
  useTableActions,
  useTableColumns,
} from '@/components';
import { forkHandler } from '@/utils';
import { Button, message, Table } from 'antd';
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import BenefitDataEditModal from './BenefitDataEditModal';
import { useDefinitionData } from './Context';
import { ALIAS_MAP, SIGN_MAP, useTableHeight } from './shared';

const COLUMNS = [
  {
    key: 'sorter',
    title: '排序',
    align: 'center',
    width: 64,
    render() {
      return (
        <DragHandle>
          <Button size='small' type='text'>
            <Icon type='menu' />
          </Button>
        </DragHandle>
      );
    },
  },
  { title: '利益名称', dataIndex: 'benefitName', key: 'benefitName', width: '25%' },
  { title: '利益代码', dataIndex: 'benefitCode', key: 'benefitCode', width: '15%', align: 'center' },
  {
    title: '利益公式',
    dataIndex: 'benefitFormula',
    key: 'benefitFormula',
    width: '15%',
    align: 'center',
  },
  {
    title: '内容类型',
    dataIndex: 'alias',
    key: 'alias',
    width: '10%',
    align: 'center',
    fallback: ALIAS_MAP.null,
    map: ALIAS_MAP,
  },
  {
    title: '字段标记',
    dataIndex: 'alias',
    key: 'sign',
    width: '10%',
    align: 'center',
    fallback: SIGN_MAP.null,
    map: SIGN_MAP,
  },
];

const ACTIONS = {
  addChild: {
    dialog: (props) => <BenefitDataEditModal {...props} resetValue={{ isShow: props.parent.isShow }} />,
    type: 1,
    itemProp: 'parent',
    text: (
      <Box component={Button} px={0.5} size='small' type='link'>
        添加下级
      </Box>
    ),
    onOk: (row, index, formData) => BenefitAPI.add(formData),
  },
  /*addSib: {
    dialog: BenefitDataEditModal,
    type: 1,
    itemProp: 'sibling',
    text: (
      <Box component={Button} px={0.5} size='small' type='link'>
        添加同级
      </Box>
    ),
    onOk: (row, index, formData) => BenefitAPI.add(formData),
  },*/
  edit: {
    dialog: BenefitDataEditModal,
    type: 1,
    itemProp: 'item',
    text: <Button type='link' icon={<Icon type='edit' />} />,
    onOk: (row, index, formData) => BenefitAPI.add(formData),
  },
  del: {
    confirm: void 0,
    beforeConfirm(row) {
      if (row?.children?.length) {
        message.warn('无法直接删除父节点');
        return false;
      }
    },
    onOk: (row) => BenefitAPI.remove([row.id]),
    text: <Button type='link' icon={<Icon type='delete' />} />,
  },
};

const reduceChildren = (list) =>
  list.reduce(
    (ret, item) => [...ret, ...(item.children?.length ? [item.id] : []), ...reduceChildren(item.children)],
    [],
  );

BenefitDataConfig = forwardRef(BenefitDataConfig);

function BenefitDataConfig(props, ref) {
  useImperativeHandle(ref, () => null, []);
  const definitionData = useDefinitionData();
  const { productCode } = definitionData?.productDefineList?.[0] ?? {};
  const xhr = useReq(BenefitAPI.getTree);
  const updateXhr = useReq(BenefitAPI.batchUpdate);

  const refresh = useCall(xhr.start, { planCode: productCode, type: 1 });

  useEffect(() => {
    if (productCode) refresh();
  }, [productCode]);

  const columns = useSimpleMemo(
    useTableColumns(COLUMNS).concat(
      useTableActions(
        ACTIONS,
        {
          onAfterAddChild: forkHandler(() => message.success('保存成功'), refresh),
          onAfterAddSib: forkHandler(() => message.success('保存成功'), refresh),
          onAfterEdit: forkHandler(() => message.success('保存成功'), refresh),
          onAfterDel: forkHandler(() => message.success('删除成功'), refresh),
        },
        { align: 'center', width: 200 },
      ),
    ),
  );

  const onAdd = useCall(
    forkHandler(
      (formData) =>
        BenefitAPI.add({
          type: 1,
          layer: 1,
          parentCode: 0,
          planCode: productCode,
          productCode,
          ...formData,
        }),
      () => {
        message.success('保存成功');
      },
      refresh,
      true,
    ),
  );

  const [wrapperRef, tableHeight] = useTableHeight();

  const components = useRef({
    body: {
      row: DraggableItem,
      wrapper: DroppableContainer,
    },
  }).current;

  const [dataSource, setData] = useState([]);
  const [expandedKeys, expandedMisc] = useSet([], true);

  const onRow = useCall((row, index) => ({ row, index }));

  const expandable = {
    indentSize: 20,
    defaultExpandAllRows: true,
    expandedRowKeys: expandedKeys,
    onExpandedRowsChange: expandedMisc.set,
    rowExpandable: useCall((row) => row.children?.length > 0),
    expandIconColumnIndex: 1,
  };

  useEffect(() => {
    if (xhr.result?.data) {
      setData(xhr.result.data);
      if (!dataSource.length) expandedMisc.set(reduceChildren(xhr.result.data));
    }
  }, [xhr.result]);

  const getDroppableKeys = useCall((allCtxMap, draggingCtx) => {
    const draggingRowKey = draggingCtx.row.id;
    const allPairs = Array.from(allCtxMap);
    const childrenKeys = [];
    const parentPair = allPairs.find(([_, { row }]) => {
      childrenKeys.push(...row.children.map((v) => v.id));
      return row.children?.some((v) => v.id === draggingRowKey);
    });

    let droppableKeys = [];
    if (!parentPair) {
      droppableKeys = allPairs.reduce((ret, [key]) => ret.concat(childrenKeys.includes(key) ? [] : [key]), []);
    } else {
      droppableKeys = parentPair[1].row.children.map((v) => v.id);
    }
    const nextExpanded = expandedKeys.filter((v) => !droppableKeys.includes(v));
    setTimeout(expandedMisc.set, 0, nextExpanded);
    return droppableKeys;
  });

  const onDrop = useCall(async (itemCtx, index, allCtxMap) => {
    console.log(itemCtx.index, index);
    const itemRowKey = itemCtx.row.id;
    const allPairs = Array.from(allCtxMap);
    const parentPair = allPairs.find(([_, { row }]) => {
      return row.children?.some((v) => v.id === itemRowKey);
    });
    let result;
    if (!parentPair) {
      const next = dataSource.slice();
      const [item] = next.splice(itemCtx.index, 1);
      next.splice(index, 0, item);
      setData(next);
      result = next;
    } else {
      const parentRow = parentPair[1].row;
      const [item] = parentRow.children.splice(itemCtx.index, 1);
      parentRow.children.splice(index, 0, item);
      setData(dataSource.slice());
      result = parentRow.children;
    }
    let shouldUpdate = false;
    result = result.map((item, index) => {
      const { children, on, ...data } = item;
      shouldUpdate = shouldUpdate || on !== index;
      return { ...data, on: index };
    });
    if (shouldUpdate) {
      await updateXhr.start(result);
      await xhr.start({ planCode: productCode, type: 1 });
    }
  });

  const loading = [xhr.status, updateXhr.status].includes('loading');

  return (
    <>
      <Box py={2} shrink={false}>
        <BenefitDataEditModal onOk={onAdd} type={1}>
          <Button type='primary' size='middle'>
            +新增配置
          </Button>
        </BenefitDataEditModal>
      </Box>
      <Box ref={wrapperRef} grow scroll>
        <DragAndDropProvider
          rowKey='id'
          itemComponent='tr'
          containerComponent='tbody'
          itemDraggingClassName='ant-table-row-selected'
          getDroppableKeys={getDroppableKeys}
          onDrop={onDrop}
        >
          <Table
            size='middle'
            bordered
            components={components}
            expandable={expandable}
            onRow={onRow}
            pagination={false}
            rowKey='id'
            loading={loading}
            dataSource={dataSource}
            columns={columns}
            scroll={{ y: Math.max(120, tableHeight - 55), x: 800 }}
          />
        </DragAndDropProvider>
      </Box>
    </>
  );
}

export default BenefitDataConfig;
